package client_tests;

import net.es.lookup.client.SimpleLS;
import net.es.lookup.common.exception.LSClientException;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.LinkedList;
import java.util.Random;

/**
 * User: Luke Lamonica
 * Date: 10/2/13
 * This is the parent class to be used for all test threads, that is, for all
 * tests that require multi-threading.
 */
public abstract class ClientThread extends Thread {

    protected ResultTransmitter rx;
    protected SimpleLS sls;
    protected String sourceHost;
    protected String targetHost;
    protected int port;
    protected int tid;
    protected String opCodeBase;
    protected String opCode;
    protected String recordID = null;
    protected LinkedList<String> recordIDs;
    protected int op_interval;
    protected int testRounds;
    protected Random rand;

    public final String TYPE = "scalability-test";

    public ClientThread(String sourceHost, String targetHost, int port, int tid, int op_interval, int testRounds) throws LSClientException {

        rx = new ResultTransmitter();
        sls = new SimpleLS(targetHost, port);
        this.sourceHost = sourceHost;
        this.targetHost = targetHost;
        this.port = port;
        this.tid = tid;
        opCodeBase = sourceHost + "-" + tid + ":";
        recordIDs = new LinkedList<String>();
        this.op_interval = op_interval;
        this.testRounds = testRounds;
        rand = new Random();
    }

    /* If run() is not overridden, the test thread will execute its
     * test-specific operations in a loop, pausing a specified duration
     * between each iteration */
    public void run() {

        //TODO: wait for barrier

        for (int i = 0; i < testRounds; i++) {

            execute();

            /*if (i % 10000 == 0) {

                clearRecords();
            }*/
        }
//        clearRecords();
    }

    /* Causes the test thread to wait a specified duration. If the duration
     * value, "op_interval", is negative, then a random value will be used for
     * the duration between each iteration. The random value will have an
     * upper bound of the absolute value of "op_interval" */
    public void pause() {

        int wait_interval = op_interval;

        if (op_interval == 0) {

            return;
        }

        if (op_interval < 0) {

            wait_interval = (rand.nextInt(-1 * op_interval));
        }

        synchronized (this) {

            try {
//System.out.println("Thread " + tid + " waiting for " + wait_interval + " milliseconds");
                this.wait(wait_interval);
            } catch (InterruptedException e) {

                System.err.println("Thread interrupted during operation wait-interval");
            }
        }
    }

    /* Sets the operation code used to label the specific test operation */
    public void setOpCode(long testStart) {

        opCode = opCodeBase + testStart;
    }

    /* Pauses and then executes the test-specific operation */
    public void execute() {

        pause();
        executeTestOp();
    }

    /* Execution method to be implemented by the specific test */
    public abstract void executeTestOp();

    /* Clears any records registered for the test */
    public void clearRecords() {

        if (!recordIDs.isEmpty()) {

            for (String id : recordIDs) {

                String action = "/lookup/" + TYPE + "/" + id;
                try {
//TODO: remove the new deleteLS and use sls
                    SimpleLS deleteLS = new SimpleLS(targetHost, port, "DELETE");
                    deleteLS.setRelativeUrl(action);
                    deleteLS.connect();
                    deleteLS.send();
                } catch (LSClientException e) {

                    System.out.println("An exception occurred while trying to delete the test record: " + id);
                    System.out.println("Delete record manually using: curl -v -H \"Content-Type: application/json\" -X DELETE " + targetHost + ":" + port + "/lookup/" + TYPE + "/" + id);
                }
            }
        }

        recordIDs.clear();
    }

    /* Calls on the ResultTransmitter to print the results to stdout */
    public void printResults(ResultTransmitter mainRx) {

        mainRx.addAll(rx);
    }
}
